/*
 * Copyright (c) 2010, okazoh_tk. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *  - Neither the name of the copyright holder nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "alkes/tree/Resource.h"
#include "alkes/tree/Element.h"

namespace alkes {

class ListenerChain {
public:
    ListenerChain(resource_ptr_base* listener, ListenerChain* next)
        : next_(next)
        , listener_(listener)
    {
    }

    ListenerChain* getNext()
    {
        return next_;
    }

    resource_ptr_base* getListener() const
    {
        return listener_;
    }

    void setNext(ListenerChain* next)
    {
        next_ = next;
    }

    Element* getElement() const
    {
        return listener_ ? listener_->getOwner() : 0;
    }

private:
    ListenerChain* next_;
    resource_ptr_base* listener_;
};


Resource::Resource()
: listeners_(0)
{
}

Resource::~Resource()
{
    destroyListeners();
}

void Resource::update(Scene* owner)
{
    onUpdate(owner);
}

void Resource::onUpdate(Scene* /*owner*/)
{
    // do nothing.
}

void Resource::notify()
{
    if (!listeners_)
        return ;

    ListenerChain* cur = listeners_;
    Element* notifier;
    while (cur != 0)
    {
        notifier = cur->getElement();
        if (notifier)
            notifier->setDirty(true);
        cur = cur->getNext();
    }
}

void Resource::addListener(resource_ptr_base* listener)
{
    if (!listener)
        return ;

    listeners_ = new ListenerChain(listener, listeners_);
}

void Resource::removeListener(resource_ptr_base* listener)
{
    if (!listeners_)
        return ;

    ListenerChain* pre = 0;
    ListenerChain* cur = listeners_;
    while (cur != 0)
    {
        if (listener == cur->getListener())
        {
            if (pre)
                pre->setNext(cur->getNext());
            else
                listeners_ = cur->getNext();

            delete cur;
            break;
        }

        pre = cur;
        cur = cur->getNext();
    }
}

void Resource::destroyListeners()
{
    if (!listeners_)
        return ;

    ListenerChain* tmp = 0;
    ListenerChain* cur = listeners_;
    Element* notifier;
    while (cur != 0)
    {
        cur->getListener()->reset();

        notifier = cur->getElement();
        if (notifier)
            notifier->setDirty(true);

        tmp = cur->getNext();
        delete cur;
        cur = tmp;
    }
}

resource_ptr_base::resource_ptr_base(Resource* ptr)
: resource_(ptr)
, owner_(0)
{
}

resource_ptr_base::~resource_ptr_base()
{
    if (resource_)
        resource_->removeListener(this);
}

void resource_ptr_base::registerResource(Element* owner, Resource* resource)
{
    if (resource_)
        resource_->removeListener(this);

    owner_ = owner;
    resource_ = resource;

    if (resource_)
        resource->addListener(this);
}

void resource_ptr_base::reset()
{
    resource_ = 0;
}

}

